package yun.web.springmvc.ext;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import yun.web.springmvc.GlobalExceptionHandlerItf;
import yun.web.springmvc.interceptor.*;
import yun.web.springmvc.paramcheck.ParamCheck;
import yun.web.springmvc.paramcheck.ParamCheckResult;
import yun.web.springmvc.paramcheck.ParamCheckException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;


/*------------------------
      author: 张天笑
      date: 2019/3/4
      time: 16:08
-------------------------*/
public class MvcServletInvocableHandlerMethod extends ServletInvocableHandlerMethod {
    public MvcServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
        super(handlerMethod);
    }

    private ServletWebRequest webRequest;

    Log log = LogFactory.get("MvcServletInvocableHandlerMethod");

    @Override
    protected Object doInvoke(Object... args) throws Exception {
        // 拿到beanFactory
        BeanFactory mvcCtx = (BeanFactory) ReflectUtil.getFieldValue(this, "beanFactory");

        HttpServletRequest req = webRequest.getRequest();
        HttpServletResponse resp = webRequest.getResponse();

        Method method = getMethod();
        Object othis = getBean();
        // 需要清除的注解
        Clear clear = method.getAnnotation(Clear.class);
        InterceptorResult interceptorBean = null;

        // clear不存在, 或clear值长度为0时, 执行拦截器
        if (clear == null || (clear.value().length > 0)) {
            // 不存在clear注解, 或 clear长度为不为0时, 才执行拦截器拦截器
            // 先获取类上的拦截器注解

            Interceptors classInterceptorsAnnotation = othis.getClass().getAnnotation(Interceptors.class);
            interceptorBean = invokeInterceptor(classInterceptorsAnnotation, clear, req, resp, mvcCtx, othis, method, args);

            // 类上拦截器链执行结果不为null
            if (interceptorBean != null) {
                // 是否通过拦截
                if (interceptorBean.flag) {
                    //通过拦截则执行method上的拦截器
                    // 获取方法上的拦截器注解
                    Interceptors methodInterceptorsAnnotation = getMethod().getAnnotation(Interceptors.class);
                    interceptorBean = invokeInterceptor(methodInterceptorsAnnotation, clear, req, resp, mvcCtx, othis, method, args);
                }
            }
            // 类上的拦截器链执行结果为null, 执行方法上的拦截器
            else {
                Interceptors methodInterceptorsAnnotation = getMethod().getAnnotation(Interceptors.class);
                interceptorBean = invokeInterceptor(methodInterceptorsAnnotation, clear, req, resp, mvcCtx, othis, method, args);
            }
        }


        // 若没有通过拦截
        if (interceptorBean != null) {
            return interceptorBean;
        }

        // 最外层try一下
        try {
            ParamCheck paramCheck = getMethodAnnotation(ParamCheck.class);
            // 若存在 ParamCheck 注解
            if (paramCheck != null) {
                ParamCheckResult paramCheckResult = null;
                // 执行校验逻辑
                String[] values = paramCheck.value();
                for (String value : values) {
                    String[] split = value.split("#");
                    if (split.length != 2) {

                    } else {
                        String clazz = split[0];
                        String checkMethodName = split[1];

                        Class<?> forName = Class.forName(clazz);
                        Object paramCheckBean = mvcCtx.getBean(forName);
                        //ReflectUtil.invoke(bean, checkMethodName, new Object[]{args});
                        try {
                            Method checoParamMethod = ReflectUtil.getMethodByName(forName, checkMethodName);
                            checoParamMethod.setAccessible(true);
                            paramCheckResult = (ParamCheckResult) checoParamMethod.invoke(paramCheckBean, args);
                        } catch (Exception e) {
                            // 抛出参数校验异常
                            throw new ParamCheckException(e);
                        }

                        //通过 参数校验, 执行下一个过滤器
                        if (paramCheckResult == null || paramCheckResult.flag) {
                        }
                        // 校验失败, 则直接把结果, 写回前端
                        else {
                            return paramCheckResult.r;
                        }
                    }
                }
                return super.doInvoke(args);
            }
            // 不存在 ParamCheck 注解
            else {
                return super.doInvoke(args);
            }
        } catch (Exception e) {
            // 全局异常拦截器
            try {
                GlobalExceptionHandlerItf globalExceptionHandler = mvcCtx.getBean(GlobalExceptionHandlerItf.class);
                // 如果没有设置全局异常拦截器
                globalExceptionHandler.handleException(req, resp, e);
            } catch (BeansException e1) {
                // 设置state为5000
                log.error(e);
                resp.setStatus(500);
            }
        }
        return null;

    }

    @Override
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        this.webRequest = webRequest;
        HttpServletRequest req = webRequest.getRequest();
        HttpServletResponse resp = webRequest.getResponse();

        // 向下执行, 调用method入参实现,
        // 然后 调用 doInvoke
        Object returnValue = null;
        try {
            returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        } catch (Exception e) {
            // 全局异常拦截器
            try {
                // 拿到beanFactory
                BeanFactory mvcCtx = (BeanFactory) ReflectUtil.getFieldValue(this, "beanFactory");

                GlobalExceptionHandlerItf globalExceptionHandler = mvcCtx.getBean(GlobalExceptionHandlerItf.class);
                globalExceptionHandler.handleException(req, resp, e);
            }
            // 如果没有设置全局异常拦截器
            catch (BeansException e1) {
                // 设置state为5000
                log.error(e);
                resp.setStatus(500);
                // 标记为已处理
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        // 处理返回结果
        new MvcResultBeanReturnValueHandler().handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);

        //mavContainer.setRequestHandled(true);
    }

    /*
    author: 张天笑 datetime: 2019/3/21 10:03
     interceptors  Interceptor注解的值, 若类上不存在clear注解, 值为 null
     clear 方法上的Clear注解
    */
    private InterceptorResult invokeInterceptor(Interceptors interceptors, Clear clear, HttpServletRequest req, HttpServletResponse resp, BeanFactory mvcCtx, Object o, Method method, Object[] args) throws Exception {
        // 类上没有拦截器注解
        if (interceptors == null || interceptors.value().length == 0) return null;

        // 存在
        if (clear != null && clear.value().length == 0) return null;

        Class<? extends Interceptor>[] classes = interceptors.value();
        for (Class<? extends Interceptor> value : classes) {
            // 包含 当前拦截器, 跳过
            if (clear != null && Arrays.asList(clear.value()).contains(value)) continue;
            try {
                Interceptor interceptor = mvcCtx.getBean(value);
                // 执行拦截器
                InterceptorResult interceptorBean = interceptor.intercept(req, resp, o, method, args);
                // 没有通过拦截器, 返回 interceptorBean
                if (interceptorBean != null && !interceptorBean.flag) {
                    return interceptorBean;
                }
            }
            // 获取不到interceptor
            catch (BeansException e) {
                throw new InterceptorNotFoundException();
            }
            // 拦截器内异常
            catch (Exception e) {
                throw e;
            }

        }
        return null;
    }


    /*------------------------
          author: 张天笑
          date: 2019/3/4
          time: 15:57
       自定义参数解析,绑定处理器
    -------------------------*/
    public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {
        HandlerMethodArgumentResolverComposite bindArgumentsResolver = new HandlerMethodArgumentResolverComposite();
        bindArgumentsResolver
                .addResolver(new MvcHandlerMethodArgumentResolver());
        super.setHandlerMethodArgumentResolvers(bindArgumentsResolver);
    }
}
